package com.atguigu.tingshu.search.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.extra.pinyin.PinyinUtil;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.FieldValue;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.aggregations.*;
import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
import co.elastic.clients.elasticsearch._types.query_dsl.TermQuery;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.CompletionSuggestOption;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.elasticsearch.core.search.HitsMetadata;
import co.elastic.clients.elasticsearch.core.search.Suggestion;
import co.elastic.clients.json.JsonData;
import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.common.utils.CollectionUtils;
import com.atguigu.tingshu.album.AlbumFeignClient;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.model.album.*;
import com.atguigu.tingshu.model.search.AlbumInfoIndex;
import com.atguigu.tingshu.model.search.AttributeValueIndex;
import com.atguigu.tingshu.model.search.SuggestIndex;
import com.atguigu.tingshu.query.search.AlbumIndexQuery;
import com.atguigu.tingshu.search.repository.AlbumInfoIndexRepository;
import com.atguigu.tingshu.search.repository.SuggestIndexRepository;
import com.atguigu.tingshu.search.service.SearchService;
import com.atguigu.tingshu.user.client.UserFeignClient;
import com.atguigu.tingshu.vo.album.AlbumStatVo;
import com.atguigu.tingshu.vo.search.AlbumInfoIndexVo;
import com.atguigu.tingshu.vo.search.AlbumSearchResponseVo;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import io.micrometer.common.util.StringUtils;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.elasticsearch.client.erhlc.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.suggest.Completion;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;


@Slf4j
@Service
@SuppressWarnings({"all"})
public class SearchServiceImpl implements SearchService {

    @Autowired
    private AlbumInfoIndexRepository albumInfoIndexRepository;
    @Autowired
    private UserFeignClient userFeignClient;
    @Autowired
    private AlbumFeignClient albumFeignClient;
    @Autowired
    /*@Qualifier("threadPoolTaskExecutor")
    private Executor executor;*/
    private Executor threadPoolTaskExecutor;

    @Autowired
    private RedissonClient redissonClient;


    /**
     * 将指定专辑上架到索引库
     *
     * @param albumId
     */
    @Override
    @SneakyThrows
    public void upperAlbum(Long albumId) {
        //1 创建需要保存的专辑对象
        AlbumInfoIndex albumInfoIndex = new AlbumInfoIndex();
        //2 调用远程接口，根据专辑id查询专辑信息
        CompletableFuture<AlbumInfo> albumInfoDataFuture = CompletableFuture.supplyAsync(() -> {
            AlbumInfo albumInfoData = albumFeignClient.getAlbumInfo(albumId).getData();
            Assert.notNull(albumInfoData, "专辑:{}不存在", albumId);
            BeanUtils.copyProperties(albumInfoData, albumInfoIndex);
            //2.1 专辑标签列表添加
            List<AlbumAttributeValue> albumAttributeValueVoList = albumInfoData.getAlbumAttributeValueVoList();
            ArrayList<AttributeValueIndex> attributeValueIndexArrayList = new ArrayList<>();
            if (CollectionUtil.isNotEmpty(albumAttributeValueVoList)) {
                for (AlbumAttributeValue albumAttributeValue : albumAttributeValueVoList) {
                    AttributeValueIndex attributeValueIndex = BeanUtil.copyProperties(albumAttributeValue, AttributeValueIndex.class);
                    attributeValueIndexArrayList.add(attributeValueIndex);
                }
            }
            albumInfoIndex.setAttributeValueIndexList(attributeValueIndexArrayList);
            return albumInfoData;
        }, threadPoolTaskExecutor);
        //3 调用远程接口，根据专辑id查询专辑分类信息
        CompletableFuture<Void> baseCategoryViewdataFuture = albumInfoDataFuture.thenAcceptAsync(albumInfoData -> {
            BaseCategoryView baseCategoryViewdata = albumFeignClient.getCategoryView(albumInfoData.getCategory3Id()).getData();
            Assert.notNull(baseCategoryViewdata, "分类：{}不存在", albumInfoData.getCategory3Id());
            //3.2 封装分类ID到索引库文档对象中
            albumInfoIndex.setCategory1Id(baseCategoryViewdata.getCategory1Id());
            albumInfoIndex.setCategory2Id(baseCategoryViewdata.getCategory2Id());
            albumInfoIndex.setCategory3Id(baseCategoryViewdata.getCategory3Id());
        }, threadPoolTaskExecutor);
        //4 调用远程接口，查询用户信息
        CompletableFuture<Void> userInfoVoDataFuture = albumInfoDataFuture.thenAcceptAsync(albumInfoData -> {
            UserInfoVo userInfoVoData = userFeignClient.getUserInfoVo(albumInfoData.getUserId()).getData();
            Assert.notNull(userInfoVoData, "用户：{}信息为空", albumInfoData.getUserId());
            albumInfoIndex.setAnnouncerName(userInfoVoData.getNickname());
        }, threadPoolTaskExecutor);

        //5 设置专辑的各个方面统计数量
        CompletableFuture<Void> statNumFuture = CompletableFuture.runAsync(() -> {
            /*Integer playStatNum = RandomUtil.randomInt(1000, 2000);
            Integer subscribeStatNum = RandomUtil.randomInt(100, 500);
            Integer buyStatNum = RandomUtil.randomInt(100, 500);
            Integer commentStatNum = RandomUtil.randomInt(100, 500);*/
            AlbumStatVo albumStatVo = albumFeignClient.getAlbumStatVoById(albumId).getData();
            Assert.notNull(albumStatVo, "用户：{}信息为空", albumId);
            Integer playStatNum = albumStatVo.getPlayStatNum();
            Integer subscribeStatNum = albumStatVo.getSubscribeStatNum();
            Integer buyStatNum = albumStatVo.getBuyStatNum();
            Integer commentStatNum = albumStatVo.getCommentStatNum();

            BigDecimal playStatNumBD = BigDecimal.valueOf(playStatNum);
            BigDecimal subscribeStatNumBD = BigDecimal.valueOf(subscribeStatNum);
            BigDecimal buyStatNumBD = BigDecimal.valueOf(buyStatNum);
            BigDecimal commentStatNumBD = BigDecimal.valueOf(commentStatNum);
            BigDecimal hotScore = playStatNumBD.multiply(BigDecimal.valueOf(0.1))
                    .add(subscribeStatNumBD.multiply(BigDecimal.valueOf(0.2)))
                    .add(buyStatNumBD.multiply(BigDecimal.valueOf(0.3)))
                    .add(commentStatNumBD.multiply(BigDecimal.valueOf(0.4)));
            albumInfoIndex.setPlayStatNum(playStatNum);
            albumInfoIndex.setSubscribeStatNum(subscribeStatNum);
            albumInfoIndex.setBuyStatNum(buyStatNum);
            albumInfoIndex.setCommentStatNum(commentStatNum);
            albumInfoIndex.setHotScore(hotScore.doubleValue());
        }, threadPoolTaskExecutor);

        //6 等待所有任务执行完成
        CompletableFuture.allOf(albumInfoDataFuture,
                baseCategoryViewdataFuture,
                userInfoVoDataFuture,
                statNumFuture).join();
        // 保存专辑对象
        albumInfoIndexRepository.save(albumInfoIndex);


        //3.将专辑标题存入提词索引库
        this.saveSuggestIndex(albumInfoIndex);

        //将新增的商品SKUID存入布隆过滤器
        //获取布隆过滤器，将新增skuID存入布隆过滤器
        RBloomFilter<Long> bloomFilter = redissonClient.getBloomFilter(RedisConstant.ALBUM_BLOOM_FILTER);
        bloomFilter.add(albumInfoIndex.getId());
    }

    /**
     * 将指定专辑上架到索引库
     *
     * @param albumId
     */
    @Override
    @SneakyThrows
    public void addAlbumNum(Long albumId, String statType) {
        //1 创建需要保存的专辑对象
        AlbumInfoIndex albumInfoIndex = new AlbumInfoIndex();
        //2 调用远程接口，根据专辑id查询专辑信息
        CompletableFuture<AlbumInfo> albumInfoDataFuture = CompletableFuture.supplyAsync(() -> {
            AlbumInfo albumInfoData = albumFeignClient.getAlbumInfo(albumId).getData();
            Assert.notNull(albumInfoData, "专辑:{}不存在", albumId);
            BeanUtils.copyProperties(albumInfoData, albumInfoIndex);
            //2.1 专辑标签列表添加
            List<AlbumAttributeValue> albumAttributeValueVoList = albumInfoData.getAlbumAttributeValueVoList();
            ArrayList<AttributeValueIndex> attributeValueIndexArrayList = new ArrayList<>();
            if (CollectionUtil.isNotEmpty(albumAttributeValueVoList)) {
                for (AlbumAttributeValue albumAttributeValue : albumAttributeValueVoList) {
                    AttributeValueIndex attributeValueIndex = BeanUtil.copyProperties(albumAttributeValue, AttributeValueIndex.class);
                    attributeValueIndexArrayList.add(attributeValueIndex);
                }
            }
            albumInfoIndex.setAttributeValueIndexList(attributeValueIndexArrayList);
            return albumInfoData;
        }, threadPoolTaskExecutor);
        //3 调用远程接口，根据专辑id查询专辑分类信息
        CompletableFuture<Void> baseCategoryViewdataFuture = albumInfoDataFuture.thenAcceptAsync(albumInfoData -> {
            BaseCategoryView baseCategoryViewdata = albumFeignClient.getCategoryView(albumInfoData.getCategory3Id()).getData();
            Assert.notNull(baseCategoryViewdata, "分类：{}不存在", albumInfoData.getCategory3Id());
            //3.2 封装分类ID到索引库文档对象中
            albumInfoIndex.setCategory1Id(baseCategoryViewdata.getCategory1Id());
            albumInfoIndex.setCategory2Id(baseCategoryViewdata.getCategory2Id());
            albumInfoIndex.setCategory3Id(baseCategoryViewdata.getCategory3Id());
        }, threadPoolTaskExecutor);
        //4 调用远程接口，查询用户信息
        CompletableFuture<Void> userInfoVoDataFuture = albumInfoDataFuture.thenAcceptAsync(albumInfoData -> {
            UserInfoVo userInfoVoData = userFeignClient.getUserInfoVo(albumInfoData.getUserId()).getData();
            Assert.notNull(userInfoVoData, "用户：{}信息为空", albumInfoData.getUserId());
            albumInfoIndex.setAnnouncerName(userInfoVoData.getNickname());
        }, threadPoolTaskExecutor);

        //5 设置专辑的各个方面统计数量
        CompletableFuture<Void> statNumFuture = CompletableFuture.runAsync(() -> {
            /*Integer playStatNum = RandomUtil.randomInt(1000, 2000);
            Integer subscribeStatNum = RandomUtil.randomInt(100, 500);
            Integer buyStatNum = RandomUtil.randomInt(100, 500);
            Integer commentStatNum = RandomUtil.randomInt(100, 500);*/
            AlbumStatVo albumStatVo = albumFeignClient.getAlbumStatVoById(albumId).getData();
            Assert.notNull(albumStatVo, "用户：{}信息为空", albumId);

            Integer playStatNum = albumStatVo.getPlayStatNum();
            Integer subscribeStatNum = albumStatVo.getSubscribeStatNum();
            Integer buyStatNum = albumStatVo.getBuyStatNum();
            Integer commentStatNum = albumStatVo.getCommentStatNum();
            if (SystemConstant.TRACK_STAT_PLAY.equals(statType)) {
                playStatNum = playStatNum + 1;
            }
            BigDecimal playStatNumBD = BigDecimal.valueOf(playStatNum);
            BigDecimal subscribeStatNumBD = BigDecimal.valueOf(subscribeStatNum);
            BigDecimal buyStatNumBD = BigDecimal.valueOf(buyStatNum);
            BigDecimal commentStatNumBD = BigDecimal.valueOf(commentStatNum);
            BigDecimal hotScore = playStatNumBD.multiply(BigDecimal.valueOf(0.1))
                    .add(subscribeStatNumBD.multiply(BigDecimal.valueOf(0.2)))
                    .add(buyStatNumBD.multiply(BigDecimal.valueOf(0.3)))
                    .add(commentStatNumBD.multiply(BigDecimal.valueOf(0.4)));
            albumInfoIndex.setPlayStatNum(playStatNum);
            albumInfoIndex.setSubscribeStatNum(subscribeStatNum);
            albumInfoIndex.setBuyStatNum(buyStatNum);
            albumInfoIndex.setCommentStatNum(commentStatNum);
            albumInfoIndex.setHotScore(hotScore.doubleValue());
        }, threadPoolTaskExecutor);

        //6 等待所有任务执行完成
        CompletableFuture.allOf(albumInfoDataFuture,
                baseCategoryViewdataFuture,
                userInfoVoDataFuture,
                statNumFuture).join();
        // 保存专辑对象
        albumInfoIndexRepository.save(albumInfoIndex);


        //3.将专辑标题存入提词索引库
        this.saveSuggestIndex(albumInfoIndex);
    }

    @Autowired
    private SuggestIndexRepository suggestIndexRepository;

    /**
     * 将专辑标题存入提词索引库
     *
     * @param albumInfoIndex
     */
    @Override
    public void saveSuggestIndex(AlbumInfoIndex albumInfoIndex) {
        //1.构建索引库文档对象
        SuggestIndex suggestIndex = new SuggestIndex();
        suggestIndex.setId(albumInfoIndex.getId().toString());
        String albumTitle = albumInfoIndex.getAlbumTitle();
        suggestIndex.setTitle(albumTitle);
        suggestIndex.setKeyword(new Completion(new String[]{albumTitle}));
        //1.1 将汉字转为汉语拼音 jing dian liu sheng ji
        String albumTitlePinyin = PinyinUtil.getPinyin(albumTitle, "");
        suggestIndex.setKeywordPinyin(new Completion(new String[]{albumTitlePinyin}));
        //1.1 将汉字转为汉语拼音首字母
        String albumTitleFirstLetter = PinyinUtil.getFirstLetter(albumTitle, "");
        suggestIndex.setKeywordSequence(new Completion(new String[]{albumTitleFirstLetter}));

        //2.存入提词文档记录到提词索引库
        suggestIndexRepository.save(suggestIndex);
    }

    @Override
    public void lowerAlbum(Long albumId) {
        albumInfoIndexRepository.deleteById(albumId);
        this.suggestIndexRepository.deleteById(albumId.toString());
    }

    @Autowired
    private ElasticsearchClient elasticsearchClient;

    private final static String ALBUM_INDEX_NAME = "albuminfo";

    /**
     * 专辑搜索
     *
     * @param albumIndexQuery
     * @return
     */
    @Override
    public AlbumSearchResponseVo albumSearch(AlbumIndexQuery albumIndexQuery) {
        try {
            //1 创建查询对象,把查询条件封装到查询对象中
            SearchRequest searchRequest = this.buildDSL(albumIndexQuery);
            System.err.println("本次检索DSL：");
            System.err.println(searchRequest);
            //2 开启查询
            SearchResponse<AlbumInfoIndex> searchResponse = elasticsearchClient.search(searchRequest, AlbumInfoIndex.class);
            //3 解析返回结果
            return this.parseSearchResult(searchResponse, albumIndexQuery);
        } catch (IOException e) {
            log.error("[查询专辑]：出现错误{}", e);
            throw new RuntimeException(e);
        }
    }

    @Override
    public SearchRequest buildDSL(AlbumIndexQuery albumIndexQuery) {
        //1 创建查询对象
        SearchRequest.Builder builder = new SearchRequest.Builder();
        //1.1 设置索引表
        builder.index(ALBUM_INDEX_NAME);
        //2 创建DSL
        //2.1 取出关键字，进行query
        String keyword = albumIndexQuery.getKeyword();
        //2.1.0 创建组合三大查询条件bool查询对象(代替首两个的query和bool)
        BoolQuery.Builder allConditionBoolQueryBuilder = new BoolQuery.Builder();
        //2.1.1 如果关键字不为空，对标题、简介、作者名字进行query
        if (StringUtils.isNotBlank(keyword)) {
            //方式一
            /*builder.query(q ->
                    q.bool(b ->
                            b.must(m->
                            m.bool(bs->
                            bs.should(m1->m1.match(t->t.field("albumTitle").query(keyword)))
                            .should(m2->m2.match(t->t.field("albumIntro").query(keyword)))
                            .should(m3->m3.term(t->t.field("announcerName").value(keyword)))))));}*/

            //方式二
            allConditionBoolQueryBuilder.must(m ->
                    m.bool(bs ->
                            bs.should(m1 -> m1.match(t -> t.field("albumTitle").query(keyword)))
                                    .should(m2 -> m2.match(t -> t.field("albumIntro").query(keyword)))
                                    .should(m3 -> m3.term(t -> t.field("announcerName").value(keyword)))));
        }
        //2.1.2 判断标签是否为空，不为空则进行query
        Long category1Id = albumIndexQuery.getCategory1Id();
        Long category2Id = albumIndexQuery.getCategory2Id();
        Long category3Id = albumIndexQuery.getCategory3Id();
        if (category1Id != null) {
            //方式一
                /*builder.query(q ->
                        q.bool(b ->
                                b.filter( f ->
                                    f.term(t ->
                                            t.field("category1Id")
                                                    .value(category1Id)))));*/
            //方式二
            allConditionBoolQueryBuilder.filter(f ->
                    f.term(t ->
                            t.field("category1Id")
                                    .value(category1Id)));

            if (category2Id != null) {
                //方式一
               /*builder.query(q->
                       q.bool(b->
                               b.filter(f->
                               f.term(t->
                                       t.field("category2Id")
                                               .value(category2Id)))));*/
                //方式二
                allConditionBoolQueryBuilder.filter(f ->
                        f.term(t ->
                                t.field("category2Id")
                                        .value(category2Id)));
                if (category3Id != null) {
                    //方式一
                   /*builder.query(q->
                           q.bool(b->
                                   b.filter(f->
                                   f.term(t->
                                           t.field("category3Id")
                                                   .value(category3Id)))));*/
                    //方式二
                    allConditionBoolQueryBuilder.filter(f ->
                            f.term(t ->
                                    t.field("category3Id")
                                            .value(category3Id)));
                }
            }
        }
        //2.1.3 判读标签是否为空，不为空则进行query
        List<String> attributeList = albumIndexQuery.getAttributeList();
        if (CollectionUtil.isNotEmpty(attributeList)) {
            for (String s : attributeList) {
                String[] split = s.split(":");
                if (split.length == 2 && split != null) {
                    //方式一
                    /*builder.query(q ->
                            q.bool(b ->
                                    b.filter(f ->
                                            f.nested(n ->
                                                    n.path("attributeValueIndexList")
                                                            .query(q1 ->
                                                                    q1.bool(b1 ->
                                                                            b1.must(m1 -> m1.term(t1 -> t1.field("attributeValueIndexList.attributeId").value(split[0])))
                                                                                    .must(m1 -> m1.term(t1 -> t1.field("attributeValueIndexList.valueId").value(split[1])))))))));*/
                    //方式二
                    allConditionBoolQueryBuilder.filter(f ->
                            f.nested(n ->
                                    n.path("attributeValueIndexList")
                                            .query(q1 ->
                                                    q1.bool(b1 ->
                                                            b1.must(m1 -> m1.term(t1 -> t1.field("attributeValueIndexList.attributeId").value(split[0])))
                                                                    .must(m1 -> m1.term(t1 -> t1.field("attributeValueIndexList.valueId").value(split[1])))
                                                    )
                                            )
                            )
                    );
                }
            }
        }
        builder.query(allConditionBoolQueryBuilder.build()._toQuery());
        //2.2 取出分页信息，进行from,size
        Integer pageNo = albumIndexQuery.getPageNo();
        Integer pageSize = albumIndexQuery.getPageSize();
        builder.from((pageNo - 1) * pageSize).size(pageSize);
        //2.3 取出排序条件，进行sort
        String order = albumIndexQuery.getOrder();
        if (StringUtils.isNotBlank(order)) {

            String[] split = order.split(":");
            if (StringUtils.isNotBlank(order) && split.length == 2) {
                String sortFlag = split[1];
                switch (split[0]) {
                    case "1": {
                        builder.sort(s ->
                                s.field(f ->
                                        f.field("hotScore")
                                                .order("asc".equals(sortFlag) ? SortOrder.Asc : SortOrder.Desc)));
                        break;
                    }
                    case "2": {
                        builder.sort(s ->
                                s.field(f ->
                                        f.field("playStatNum")
                                                .order("asc".equals(sortFlag) ? SortOrder.Asc : SortOrder.Desc)));
                        break;
                    }
                    case "3": {
                        builder.sort(s ->
                                s.field(f ->
                                        f.field("createTime")
                                                .order("asc".equals(sortFlag) ? SortOrder.Asc : SortOrder.Desc)));
                        break;
                    }
                }
            }
        }

        //2.4 取出高亮的字段，进行highlight
        if (StringUtils.isNotEmpty(keyword)) {
            builder.highlight(h ->
                            h.fields("albumTitle", hf -> hf.preTags("<font style='color:red'>").postTags("</font>"))
                    /*.fields("albumIntro", hf->hf.preTags("<font style='color:red'>").postTags("</font>"))*/);
        }
        //2.5 去除不需要显示的字段
        builder.source(s -> s.filter(f -> f.excludes("announcerName", "isFinished", "category1Id", "category2Id", "category3Id", "attrbuteValueIndexList")));
        return builder.build();
    }

    /**
     * 解析查询结果
     *
     * @param searchResponse
     * @param albumIndexQuery
     * @return
     */
    @Override
    public AlbumSearchResponseVo parseSearchResult(SearchResponse<AlbumInfoIndex> searchResponse, AlbumIndexQuery albumIndexQuery) {
        //1 创建响应的对象
        AlbumSearchResponseVo albumSearchResponseVo = new AlbumSearchResponseVo();
        //2 设置vo的分页信息
        Integer pageNo = albumIndexQuery.getPageNo();
        Integer pageSize = albumIndexQuery.getPageSize();
        albumSearchResponseVo.setPageNo(pageNo);
        albumSearchResponseVo.setPageSize(pageSize);
        //2.1 解析ES获取命中记录数
        long total = searchResponse.hits().total().value();
        albumSearchResponseVo.setTotal(total);
        //2.2 计算出总页面的数量
        Long totalPage = total % pageSize == 0 ? total / pageSize : total / pageSize + 1;
        albumSearchResponseVo.setTotalPages(totalPage);
        //3 保存专辑的信息
        List<Hit<AlbumInfoIndex>> hitList = searchResponse.hits().hits();
        if (CollectionUtil.isNotEmpty(hitList)) {
            List<AlbumInfoIndexVo> list = hitList.stream().map(hit -> {
                AlbumInfoIndex albumInfoIndex = hit.source();
                //处理高亮
                Map<String, List<String>> highlightMap = hit.highlight();
                if (CollectionUtil.isNotEmpty(highlightMap)) {

                    List<String> albumTitleList = highlightMap.get("albumTitle");
                    String highlightText = albumTitleList != null && !albumTitleList.isEmpty() ? albumTitleList.get(0) : null;
/*
                    // 获取 albumIntro 的高亮文本
                    List<String> albumIntroList = highlightMap.get("albumIntro");
                    String albumIntro = albumIntroList != null && !albumIntroList.isEmpty() ? albumIntroList.get(0) : null;*/
                    albumInfoIndex.setAlbumTitle(highlightText);
                    /*albumInfoIndex.setAlbumIntro(albumIntro);*/
                }
                AlbumInfoIndexVo albumInfoIndexVo = new AlbumInfoIndexVo();
                BeanUtil.copyProperties(albumInfoIndex, albumInfoIndexVo);
                return albumInfoIndexVo;
            }).collect(Collectors.toList());
            albumSearchResponseVo.setList(list);
        }

        return albumSearchResponseVo;
    }

    private static final String INDEX_NAME = "albuminfo";
    private static final String SUGGEST_INDEX = "suggestinfo";

    /**
     * 根据用户已录入字符查询提词索引库进行自动补全关键字
     *
     * @param keyword
     * @return
     */
    @Override
    public List<String> completeSuggest(String keyword) {
        try {
            //1.发起自动补全请求
            SearchResponse<SuggestIndex> searchResponse = elasticsearchClient.search(s ->
                            s.index(SUGGEST_INDEX)
                                    .suggest(
                                            s1 -> s1.suggesters("letter-suggest", fs -> fs.prefix(keyword).completion(c -> c.field("keywordSequence").size(10).skipDuplicates(true)))
                                                    .suggesters("pinyin-suggest", s2 -> s2.prefix(keyword).completion(c -> c.field("keywordPinyin").size(10).skipDuplicates(true)))
                                                    .suggesters("keyword-suggest", s2 -> s2.prefix(keyword).completion(c -> c.field("keyword").size(10).skipDuplicates(true)))
                                    )
                    , SuggestIndex.class);
            //2.解析ES自动补全结果
            Set<String> hashSet = new HashSet<>();
            //2.1 解析建议结果-通过不同建议参数名获取汉字、拼音等提示词结果
            hashSet.addAll(this.parseSuggestResult(searchResponse, "letter-suggest"));
            hashSet.addAll(this.parseSuggestResult(searchResponse, "pinyin-suggest"));
            hashSet.addAll(this.parseSuggestResult(searchResponse, "keyword-suggest"));
            //2.2 如果解析建议提示词列表长度小于10，采用全文查询尝试补全到10个
            if (hashSet.size() < 10) {
                //2.2.1 根据用户录入字符进行全文检索
                SearchResponse<AlbumInfoIndex> matchSearchResponse = elasticsearchClient.search(
                        s -> s.index(INDEX_NAME)
                                .query(q -> q.match(m -> m.field("albumTitle").query(keyword)))
                                .size(10)
                        , AlbumInfoIndex.class
                );
                HitsMetadata<AlbumInfoIndex> hits = matchSearchResponse.hits();
                List<Hit<AlbumInfoIndex>> hitList = hits.hits();
                if (CollectionUtil.isNotEmpty(hitList)) {
                    for (Hit<AlbumInfoIndex> hit : hitList) {
                        AlbumInfoIndex source = hit.source();
                        //2.2.2 将检索到专辑标题内容加入到提词结果列表中
                        hashSet.add(source.getAlbumTitle());
                        if (hashSet.size() >= 10) {
                            break;
                        }
                    }
                }
            }
            if (hashSet.size() >= 10) {
                //如果提词结果列表中大于10截取前10个
                return new ArrayList<>(hashSet).subList(0, 10);
            } else {
                return new ArrayList<>(hashSet);
            }
        } catch (IOException e) {
            log.error("[搜索服务]关键字自动补全异常：{}", e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 解析建议词结果
     *
     * @param searchResponse ES检索结果对象
     * @param suggestName    自定义建议词参数名称
     * @return
     */
    @Override
    public List<String> parseSuggestResult(SearchResponse<SuggestIndex> searchResponse, String suggestName) {
        //根据自定义建议词参数名称获取结果列表
        List<String> list = new ArrayList<>();
        List<Suggestion<SuggestIndex>> suggestionList = searchResponse.suggest().get(suggestName);
        if (CollectionUtil.isNotEmpty(suggestionList)) {
            //遍历得到建议对象
            for (Suggestion<SuggestIndex> suggestIndexSuggestion : suggestionList) {
                for (CompletionSuggestOption<SuggestIndex> option : suggestIndexSuggestion.completion().options()) {
                    SuggestIndex suggestIndex = option.source();
                    list.add(suggestIndex.getTitle());
                }
            }
        }
        return list;
    }

    /**
     * 查询置顶3级分类下热度TOP6专辑列表
     *
     * @param category1Id
     * @return
     */
    @Override
    public List<Map<String, Object>> searchTopCategoryHotAlbum(Long category1Id) {
        try {
            //1.远程调用专辑服务获取置顶7个三级分类列表 获取三级分类ID列表
            List<BaseCategory3> baseCategory3List = albumFeignClient.getTopBaseCategory3(category1Id).getData();
            Assert.notNull(baseCategory3List, "获取置顶7个三级分类列表失败");

            //1.1 将三级分类集合转为泛型：FieldValue
            List<FieldValue> fieldValueList = baseCategory3List
                    .stream()
                    .map(baseCategory3 -> FieldValue.of(baseCategory3.getId()))
                    .collect(Collectors.toList());

            //1.2 为了解析结果封装分类对象，将分类List集合转为Map  Map的Key就是三级分类ID Map的Value就是三级分类对象
            Map<Long, BaseCategory3> baseCategory3Map = baseCategory3List
                    .stream()
                    .collect(Collectors.toMap(c3 -> c3.getId(), c3 -> c3));

            //2.检索ES，查询条件：7个置顶三级分类ID，聚合：先根据三级分类ID聚合，设置子聚合根据热度排序获取前6个专辑
            SearchResponse<AlbumInfoIndex> searchResponse = elasticsearchClient.search(
                    s -> s.index(INDEX_NAME)
                            .size(0)
                            .query(q -> q.terms(t -> t.field("category3Id").terms(t1 -> t1.value(fieldValueList))))
                            .aggregations(
                                    "c3_agg", a -> a.terms(t -> t.field("category3Id").size(10))
                                            .aggregations(
                                                    "top6_agg", a1 -> a1.topHits(t -> t.sort(sort -> sort.field(f -> f.field("hotScore").order(SortOrder.Desc))).size(6).source(so -> so.filter(f -> f.excludes("attributeValueIndexList",
                                                            "createTime",
                                                            /*"playStatNum",*/
                                                            "subscribeStatNum",
                                                            "buyStatNum",
                                                            "commentStatNum",
                                                            "hotScore"))))
                                            )
                            )
                    , AlbumInfoIndex.class);
            //3.解析ES响应结果，封装置顶分类热门专辑列表
            //3.1 获取三级分类对应聚合结果对象
            Aggregate category3Aggregate = searchResponse.aggregations().get("c3_agg");
            //3.2 获取三级分类ID聚合结果bucket桶数组 每遍历一个Bucket封装置顶分类热门专辑Map对象
            Buckets<LongTermsBucket> category3Buckets = category3Aggregate.lterms().buckets();
            List<LongTermsBucket> category3BucketList = category3Buckets.array();
            if (CollectionUtil.isNotEmpty(category3BucketList)) {
                List<Map<String, Object>> list = category3BucketList
                        .stream()
                        .map(bucket -> {
                            //获取对应三级分类ID
                            long category3Id = bucket.key();
                            //3.3 从三级分类聚合对象bucket中获取“top6_agg”子聚合
                            List<Hit<JsonData>> top6AggHitList = bucket.aggregations().get("top6_agg").topHits().hits().hits();
                            //3.4 获取子聚合结果bucket桶数组 包含就是热度前6个专辑
                            if (CollectionUtil.isNotEmpty(top6AggHitList)) {
                                //3.4.1 将热度前6的专辑类型从Hit转为AlbumInfoIndex
                                List<AlbumInfoIndex> top6AlbumList = top6AggHitList
                                        .stream()
                                        .map(hit -> {
                                            String jsonSource = hit.source().toString();
                                            return JSON.parseObject(jsonSource, AlbumInfoIndex.class);
                                        }).collect(Collectors.toList());
                                //3.5 封装置顶分类热门专辑Map对象
                                Map<String, Object> map = new HashMap<>();
                                //TODO 如何根据三级分类ID拿到分类对象
                                map.put("baseCategory3", baseCategory3Map.get(category3Id));
                                map.put("list", top6AlbumList);
                                return map;
                            }
                            return null;
                        }).collect(Collectors.toList());
                return list;
            }
            return null;
        } catch (IOException e) {
            log.error("[搜索服务]聚合置顶分类热门专辑异常：{}", e);
            throw new RuntimeException(e);
        }
    }

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 更新不同分类不同排行TOP20数据
     *
     * @return
     */
    @Override
    public void updateLatelyAlbumRanking() {
        try {
            //1.远程调用专辑服务获取所有1级分类获取一级分类ID列表
            List<BaseCategory1> baseCategory1List = albumFeignClient.getAllCategory1().getData();
            //2 获取一级分类的id
            List<Long> category1IdList = baseCategory1List.stream()
                    .map(baseCategory1 -> baseCategory1.getId()).collect(Collectors.toList());
            //3 处理某个一级分类排行数据，遍历5种不同排序字段
            String[] rankingDimensionArray = new String[]{"hotScore", "playStatNum", "subscribeStatNum", "buyStatNum", "commentStatNum"};
            //4 遍历一级分类ID列表
            for (Long category1Id : category1IdList) {
                //4 创建redis的key
                String key = RedisConstant.RANKING_KEY_PREFIX + category1Id;
                //5 遍历排序字段
                for (String rankingDimension : rankingDimensionArray) {
                    SearchResponse<AlbumInfoIndex> albumInfoIndexSearchResponse = elasticsearchClient.search(s ->
                            s.index(INDEX_NAME).query(q -> q.term(t -> t.field("category1Id").value(category1Id)))
                                    .sort(s1 -> s1.field(f -> f.field(rankingDimension).order(SortOrder.Desc)))
                                    .size(20)
                                    .source(so -> so.filter(f -> f.excludes("attributeValueIndexList",
                                            "createTime",
                                            "playStatNum",
                                            "subscribeStatNum",
                                            "buyStatNum",
                                            "commentStatNum",
                                            "hotScore"))), AlbumInfoIndex.class);
                    List<Hit<AlbumInfoIndex>> hitList = albumInfoIndexSearchResponse.hits().hits();
                    List<AlbumInfoIndexVo> albumInfoIndexVoList = hitList.stream().map(hit -> {
                        AlbumInfoIndex albumInfoIndex = hit.source();
                        Map<String, List<String>> highlight = hit.highlight();
                        if (CollectionUtil.isNotEmpty(highlight)) {
                            albumInfoIndex.setAlbumTitle(highlight.get("albumTitle").get(0));
                        }
                        return BeanUtil.copyProperties(albumInfoIndex, AlbumInfoIndexVo.class);
                    }).collect(Collectors.toList());

                    redisTemplate.opsForHash().put(key, rankingDimension, albumInfoIndexVoList);

                }
            }
        } catch (Exception e) {
            log.error("[搜索服务]更新排行榜Redis异常：{}", e);
            throw new RuntimeException(e);
        }


    }

    /**
     * 查询排行榜
     *
     * @param category1Id
     * @param dimension
     * @return
     */
    @Override
    public List<AlbumInfoIndexVo> findRankingList(Long category1Id, String dimension) {

        String key = RedisConstant.RANKING_KEY_PREFIX + category1Id;
        List<AlbumInfoIndexVo> albumInfoIndexList = (List<AlbumInfoIndexVo>)redisTemplate.opsForHash().get(key, dimension);
        return albumInfoIndexList;
    }
}
